package com.mico.echart.monitor.model;

import com.mico.echart.monitor.config.GlobalObject;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @author micocube
 * projectName: springboot-example
 * packageName: com.mico.echart.monitor.model
 * email: ldscube@gmail.com
 * createTime: 2020-01-14 17:09
 * version: 0.1
 * description:
 */
public class Threads {


    private static ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
    private static RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
    private static OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
    private static final DecimalFormat df = new DecimalFormat("#0.00");

    public static LinkedHashMap<Long,Threads> threadsMap = new LinkedHashMap<Long, Threads>();
    private long preThreadCpuTime;
    private long preThreadUserTime;
    private long preUpTime;
    public float sysCpuPercent;
    private float userCpuPercent;
    private String name;
    private Thread.State state;
    private int priority;
    private long id;
    private final long processId = Integer.valueOf(runtimeMXBean.getName().split("@")[0]).intValue();;
    private boolean alive;
    private boolean daemon;
    private boolean interrupted;
    private StringBuffer stackTrace;

    static{
        initThreadStack();
    }


    public static void initThreadStack() {
        Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
        Set<Thread> threads = allStackTraces.keySet();
        threads.forEach(thread -> {
            StringBuffer buffer = new StringBuffer();
            long threadCpuTime = threadBean.getThreadCpuTime(thread.getId());
            long threadUserTime = threadBean.getThreadUserTime(thread.getId());
            buffer.append("\tcpu time(ns)："+threadCpuTime+",user time(ns):"+threadUserTime+"\n");
            StackTraceElement[] stackTrace = thread.getStackTrace();
            Arrays.stream(stackTrace).forEach(stackTraceElement -> {
                buffer.append("\t"+stackTraceElement+"\n");
            });

            Set<Long> tids = threadsMap.keySet();

            Long tid = Long.valueOf(thread.getId());
            long uptime = runtimeMXBean.getUptime();
            if(tids.contains(tid)){
                Threads preThreads = threadsMap.get(tid);
                long preThreadCpuTime = preThreads.getPreThreadCpuTime();
                long preThreadUserTime = preThreads.getPreThreadUserTime();
                long preUpTime = preThreads.getPreUpTime();

                long elapsedCpu = threadCpuTime - preThreadCpuTime;
                long userElapsedCpu = threadUserTime - preThreadUserTime;

                long elaspedTime = uptime - preUpTime;

                float cpuUsage = Math.min(99F, elapsedCpu / (elaspedTime * 10000F * operatingSystemMXBean.getAvailableProcessors()));


                float cpuUserUsage = Math.min(99F, userElapsedCpu / (elaspedTime * 10000F * operatingSystemMXBean.getAvailableProcessors()));
                preThreads.setPreUpTime(uptime);
                preThreads.setPreThreadCpuTime(threadCpuTime);
                preThreads.setPreThreadUserTime(threadUserTime);
                preThreads.setUserCpuPercent(cpuUserUsage);
                preThreads.setSysCpuPercent(cpuUsage);
            }else {
                Threads threadInfo = new Threads(threadCpuTime, threadUserTime,
                        thread.getName(), thread.getState(), thread.getPriority(),
                        thread.isAlive(), thread.isDaemon(), thread.isInterrupted(),
                        buffer);

                threadInfo.setPreUpTime(uptime);
                threadInfo.setSysCpuPercent(0.0f);
                threadInfo.setUserCpuPercent(0.0f);

                threadsMap.put(tid,threadInfo);
            }


        });
    }


    public long getPreThreadCpuTime() {
        return preThreadCpuTime;
    }

    public Threads setPreThreadCpuTime(long preThreadCpuTime) {
        this.preThreadCpuTime = preThreadCpuTime;
        return this;
    }

    public long getPreThreadUserTime() {
        return preThreadUserTime;
    }

    public Threads setPreThreadUserTime(long preThreadUserTime) {
        this.preThreadUserTime = preThreadUserTime;
        return this;
    }

    public String getName() {
        return name;
    }

    public Threads setName(String name) {
        this.name = name;
        return this;
    }

    public Thread.State getState() {
        return state;
    }

    public Threads setState(Thread.State state) {
        this.state = state;
        return this;
    }

    public int getPriority() {
        return priority;
    }

    public Threads setPriority(int priority) {
        this.priority = priority;
        return this;
    }

    public boolean getAlive() {
        return alive;
    }

    public Threads setAlive(boolean alive) {
        this.alive = alive;
        return this;
    }

    public boolean getDaemon() {
        return daemon;
    }

    public Threads setDaemon(boolean daemon) {
        this.daemon = daemon;
        return this;
    }

    public boolean getInterrupted() {
        return interrupted;
    }

    public Threads setInterrupted(boolean interrupted) {
        this.interrupted = interrupted;
        return this;
    }

    public StringBuffer getStackTrace() {
        return stackTrace;
    }

    public Threads setStackTrace(StringBuffer stackTrace) {
        this.stackTrace = stackTrace;
        return this;
    }

    public long getPreUpTime() {
        return preUpTime;
    }

    public Threads setPreUpTime(long preUpTime) {
        this.preUpTime = preUpTime;
        return this;
    }

    public String getSysCpuPercent() {
        return df.format(sysCpuPercent)+"%";
    }

    public Threads setSysCpuPercent(float sysCpuPercent) {
        this.sysCpuPercent = sysCpuPercent;
        return this;
    }

    public String getUserCpuPercent() {
        return df.format(userCpuPercent)+"%";
    }

    public Threads setUserCpuPercent(float userCpuPercent) {
        this.userCpuPercent = userCpuPercent;
        return this;
    }

    public long getId() {
        return id;
    }

    public Threads setId(long id) {
        this.id = id;
        return this;
    }

    public long getProcessId() {
        return processId;
    }

    public Threads(long preThreadCpuTime, long preThreadUserTime, String name, Thread.State state, int priority, boolean alive, boolean daemon, boolean interrupted, StringBuffer stackTrace) {
        this.preThreadCpuTime = preThreadCpuTime;
        this.preThreadUserTime = preThreadUserTime;
        this.name = name;
        this.state = state;
        this.priority = priority;
        this.alive = alive;
        this.daemon = daemon;
        this.interrupted = interrupted;
        this.stackTrace = stackTrace;
    }
}
